////////////////////////////////////////////////////////////////////////////////
// This file is part of the Mist compiler.                                    //
//                                                                            //
// Copyright (C) 2008, 2009 The Mist project                                  //
//                                                                            //
// For a list of contributors, see the COMMITTERS file.                       //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// in the COPYING file. If not, see <http://www.gnu.org/licenses/>.           //
////////////////////////////////////////////////////////////////////////////////

///
/// \file
/// Contains the parse function, the first two phases of the compilation process.
///

#ifndef PARSE_HPP
#define PARSE_HPP

////////////////////////////////////////////////////////////////////////////////
// Includes                                                                   //
////////////////////////////////////////////////////////////////////////////////

#include <istream>
using std::istream;

#include <string>
using std::string;

#include <sstream>
using std::istringstream;

#include <boost/scoped_ptr.hpp>
using boost::scoped_ptr;

#include <typeinfo>

#include "common/ptr.hpp"
#include "common/exception.hpp"
#include "common/assert.hpp"
#include "lexer.lex.hpp"
#include "messages.hpp"
#include "parser.tab.hpp"
#include "setparents.hpp"
#include "tree.hpp"

////////////////////////////////////////////////////////////////////////////////
// Extra Types                                                                //
////////////////////////////////////////////////////////////////////////////////

/// An exception that is thrown if an error occurred during the parsing process.
///
DEFINE_EXCEPTION(ParserException, "an error occurred during the parsing process");

////////////////////////////////////////////////////////////////////////////////
// parse Function                                                             //
////////////////////////////////////////////////////////////////////////////////

/// Parse Mist source code, assuming to find a \a T. Encapsulates both the
/// lexer and the parser.
///
/// \return The abstract syntax tree associated with the source code.
///
template <class T>
sptr<T> parse(
	istream& stream,                            ///< the input stream with the source-code to parse
	wptr<Symbol> scope,                         ///< the scope of the resulting ast
	vector<sptr<Expression> > insertions = vector<sptr<Expression> >(), /// placeholders to insert
	wptr<Messages> messages = wptr<Messages>(), ///< the compiler message collector; if the null pointer is used, no messages are recorded
	const string& filename = string()           ///< the filename to record for messages
) throw (ParserException)                       ///< if an error occurred during the parsing process
;

/// Parse Mist source code, assuming to find a \a T. Encapsulates both the
/// lexer and the parser.
///
/// \return The abstract syntax tree associated with the source code.
///
template <class T>
sptr<T> parse(
	const string& code,                         ///< the source-code to parse
	wptr<Symbol> scope,                         ///< the scope of the resulting ast
	vector<sptr<Expression> > insertions = vector<sptr<Expression> >(), /// placeholders to insert
	wptr<Messages> messages = wptr<Messages>(), ///< the compiler message collector; if the null pointer is used, no messages are recorded
	const string& filename = string()           ///< the filename to record for messages
) throw (ParserException)                       ///< if an error occurred during the parsing process
;

////////////////////////////////////////////////////////////////////////////////
// Turning on Parser Debug Information                                        //
////////////////////////////////////////////////////////////////////////////////

/// \def DEBUG_PARSER
/// If this macro is defined in the parse.hpp file, Bison debug information
/// will be printed during the compiler parsing phase.
///
/*/
#define DEBUG_PARSER
//*/

////////////////////////////////////////////////////////////////////////////////
// Function Implementation                                                    //
////////////////////////////////////////////////////////////////////////////////

namespace {
	/// Given AST type \a T, return the corresponding entrypoint token value.
	///
	template <class T>
	int entry_point() {
		#define ENTRY_POINT(Class, Token) \
			if (typeid(T) == typeid(Class)) \
				return yy::Parser::token::Token;
		
		ENTRY_POINT( Declaration , ENTER_DECLARATION  )
		ENTRY_POINT( Declarations, ENTER_DECLARATIONS )
		ENTRY_POINT( FunctionType, ENTER_FUNCTIONTYPE )
		ENTRY_POINT( Statement   , ENTER_STATEMENT    )
		ENTRY_POINT( Expression  , ENTER_EXPRESSION   )
		ENTRY_POINT( Reference   , ENTER_REFERENCE    )
		ENTRY_POINT( Type        , ENTER_TYPE         )
		
		assert(false);
		
		#undef ENTRY_POINT
	}
}

template <class T>
sptr<T> parse(istream& stream, wptr<Symbol> scope, vector<sptr<Expression> > insertions, wptr<Messages> messages, const string& filename) throw (ParserException) {
	assert(scope);
	
	yyscan_t scanner;
	
	scoped_ptr<LexerParams> lexerParams(new LexerParams);
	lexerParams->in = &stream;
	lexerParams->messages = messages;
	lexerParams->entry_point = entry_point<T>();
	yylex_init_extra (lexerParams.get(), &scanner);
	
	sptr<Node> result;
	
	scoped_ptr<yy::Parser> parser(new yy::Parser(result, scanner, messages, filename, insertions));
	
	#ifdef DEBUG_PARSER
		parser->set_debug_level(true);
	#endif
	
	int parseSuccess = parser->parse();
	
	yylex_destroy(scanner);
	
	if (parseSuccess == 1)
		throw ParserException();
	else if (parseSuccess == 2)
		throw ParserException("The parser ran out of memory.");
	
	set_parents(result);
	result->scope = scope;
	
	return result.cast<T>();
}

template <class T>
sptr<T> parse(const string& code, wptr<Symbol> scope, vector<sptr<Expression> > insertions, wptr<Messages> messages, const string& filename) throw (ParserException) {
	assert(scope);
	
	istringstream strout(code);
	
	sptr<T> result = parse<T>(strout, scope, insertions, messages, filename);
	
	return result;
}

#endif // PARSE_HPP
